package com.strongbj.iot.devices.amazondb.request.handle;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import com.strongbj.core.util.ByteUtil;
import com.strongbj.core.util.ContextUtils;
import com.strongbj.iot.devices.amazondb.entity.Tag;
import com.strongbj.iot.devices.amazondb.service.MongoDBService;
import com.strongbj.iot.devices.amazonreader.message.ReaderMessageFactory;
import com.strongbj.iot.devices.amazonreader.request.message.MQBodyMessage;
import com.strongbj.iot.devices.reader.server.ReaderServer;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelMatcher;

/**
 * 盘点处理
 * 
 * @author root
 *
 */
public class InventoryRunnable implements Runnable {
	private static Logger logger = LogManager.getLogger(InventoryRunnable.class.getName());
	private ReaderMessageFactory rmFactory = new ReaderMessageFactory();
	private MongoDBService mongoDBService = (MongoDBService) ContextUtils.getBean("mongoDBService");
	private static final long LAB_MAX_UPDATE_TIME_INTERVAL = 1800000;
	private static final long SEND_CLOSE_LAG_WAIT_TIME = 30000;
	private int num = 0;
	private String readerCode;
	private MQBodyMessage<Object> t;
	private Set<String> onLineTagSet, offlineTabSet;
	private Map<String, Set<String>> tagsMap;

	public InventoryRunnable(String readerCode, MQBodyMessage<Object> t, Map<String, Set<String>> tagsMap) {
		this.readerCode = readerCode;
		this.t = t;
		this.tagsMap = tagsMap;
		
	}

	@Override
	public void run() {
		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss"); 
		try {
			this.onLineTagSet = tagsMap.get(t.getGUID() + "_online");
			this.offlineTabSet = tagsMap.get(t.getGUID() + "_offline");
			Date now = new Date();
			Query query = new Query(Criteria.where("readerCode").is(this.readerCode));
//				.and("updateTime").gte(new Date(now.getTime() - 30 * 60 * 1000)));
			List<Tag> tags = mongoDBService.find(query);
			List<String> offLineLabList = new ArrayList<>();
			List<String> onLineLagList = new ArrayList<>();
			if (tags != null && tags.size() > 0) {
				for (Tag tag : tags) {
					if (tag.getUpdateTime() == null
							|| now.getTime() - tag.getUpdateTime().getTime() > LAB_MAX_UPDATE_TIME_INTERVAL) {
						offLineLabList.add(tag.getId());
						logger.info("插入离线队列标签[{}] udpateTime={}", tag.getId(),sdf.format(tag.getUpdateTime()));
					} else {
						onLineLagList.add(tag.getId());
						logger.info("插入在线队列标签[{}] udpateTime={}", tag.getId(),sdf.format(tag.getUpdateTime()));
					}
				}
			}
			this.onLineTagSet.addAll(onLineLagList);
			List<String> offlineTabList = this.closeLab(readerCode, onLineLagList, offLineLabList, this.onLineTagSet);
			this.offlineTabSet.addAll(offlineTabList);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param readerCode
	 * @param onlineLabs
	 * @param offlineLabs
	 * @param onLineTagSet
	 * @return 返回离线标签数组
	 * @throws Exception
	 */
	private List<String> closeLab(String readerCode, List<String> onlineLabs, List<String> offlineLabs,
			Set<String> onLineTagSet) throws Exception {
		Date now = new Date();
		if (offlineLabs.size() > 0) {
			if (offlineLabs.size() > 4) {
				int pageSize = 5;
				int total = offlineLabs.size();
				int pageCount = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
				for (int i = 0; i < pageCount; i++) {
					int start = i * pageSize;
					int end = start + pageSize > total ? total : start + pageSize;
					List<String> subList = offlineLabs.subList(start, end);
					this.sendReaderCommand(readerCode, subList);
					Thread.sleep(15000);
				}
			} else {
				this.sendReaderCommand(readerCode, offlineLabs);
			}
			Thread.sleep(SEND_CLOSE_LAG_WAIT_TIME); // 等1分钟
			Iterator<String> it = offlineLabs.iterator();
			while (it.hasNext()) {
				String lab = it.next();
				Tag tag = mongoDBService.findById(lab);
				if (tag.getUpdateTime() != null
						&& now.getTime() - tag.getUpdateTime().getTime() < LAB_MAX_UPDATE_TIME_INTERVAL) {
					it.remove(); // 从离线列表删除
					onlineLabs.add(lab); // 添加到在线列表
				}
			}

			this.onLineTagSet.addAll(onlineLabs);

			num++;
			if (num < 5) {
				return this.closeLab(readerCode, onlineLabs, offlineLabs, onLineTagSet);
			}
		}
		num = 0;
		return offlineLabs;
	}

	private void sendReaderCommand(String readerCode, List<String> offlineLabs) throws Exception {
		ByteBuf bs = Unpooled.copiedBuffer(this.messageDatasToBytes(readerCode, offlineLabs));
		String hexCommand = ByteUtil.byteArrToHexString(bs.array());
		ReaderServer.channels.writeAndFlush(bs, new MyChannelMatchers());
		logger.info("将点灭消息转换为Reader协议并发送给Reader:" + hexCommand);
	}

	/**
	 * mq消息转byte数组
	 */
	private byte[] messageDatasToBytes(String devSn, List<String> labs) throws Exception {
		final int LABEL_DATA_SIZE = 5; // 一个点亮标签所占的数据大小
		byte[] data = new byte[LABEL_DATA_SIZE * labs.size()];

		// 将2.4g标签从“0.0.0.0”格式转为byte数组(如传过来的标签格式错误，将抛出异常)
		for (int i = 0; i < labs.size(); i++) {
			String cl = labs.get(i);
			String[] temp = cl.split("\\.");
			for (int j = 0; j < temp.length; j++) {
				data[i * LABEL_DATA_SIZE + j] = Integer.valueOf(temp[j]).byteValue();
			}

			// 第5位设置是否点亮
			byte light = Integer.valueOf("F0", 16).byteValue();
			data[i * 5 + 4] = light; // 31为二进制00011111；15为00001111
		}

		logger.info("点灭标签的编码：" + ByteUtil.byteArrToHexString(data));

		return rmFactory.createReaderMessage(devSn, (byte) 10, data);
	}

	class MyChannelMatchers implements ChannelMatcher {
//		private CallLabel callLabel;
		public MyChannelMatchers() {

		}
//		public MyChannelMatchers(CallLabel callLabel){
//			this.callLabel = callLabel;
//		}

		@Override
		public boolean matches(Channel channel) {
//			InetSocketAddress insocket = (InetSocketAddress)channel.remoteAddress();
//			if(insocket.getAddress().getHostAddress().equals(this.callLabel.getDevIp())){
//				return true;
//			}else{
//				return false;
//			}
			return true;
		}
	}
}
